//package hos.map.tool;
//
//import java.util.List;
//
//import hos.map.coordinate.LatLng;
//import hos.map.geometry.Point;
//
///**
// * <p>Title: SphericalUtil </p>
// * <p>Description:  </p>
// * <p>Company: www.mapuni.com </p>
// *
// * @author : 蔡俊峰
// * @version : 1.0
// * @date : 2023-12-06 22:12
// */
//public class SphericalUtil {
//    private SphericalUtil() {
//    }
//
//    private static double getEarthRadius() {
//        return MathUtil.getEarthRadius();
//    }
//
//    private static double getPI() {
//        return MathUtil.getPI();
//    }
//
//    /**
//     * 计算经纬度之间的距离
//     */
//    public static double distance(double startLat, double startLon, double endLat, double endLon) {
//        final double dLat = MathUtil.toRadians(endLat - startLat);
//        final double dLon = MathUtil.toRadians(endLon - startLon);
//        final double a = Math.pow(Math.sin(dLat / 2), 2) +
//                Math.pow(Math.sin(dLon / 2), 2) *
//                        Math.cos(MathUtil.toRadians(startLat)) *
//                        Math.cos(MathUtil.toRadians(endLat));
//        final double c = 2 * Math.asin(Math.sqrt(a));
//        return 6378137.0 * c;
//    }
//
//    /**
//     * 计算起点和终点之间的方位。
//     */
//    public static double computeHeading(LatLng start, LatLng end) {
//        return computeHeading(start.getLatitude(), start.getLongitude(), end.getLatitude(), end.getLongitude());
//    }
//
//    /**
//     * 计算起点和终点之间的方位。
//     */
//    public static double computeHeading(Point start, Point end) {
//        return computeHeading(start.getY(), start.getX(), end.getY(), end.getX());
//    }
//
//    /**
//     * 计算起点和终点之间的方位。
//     */
//    public static double computeHeading(double startLat, double startLon, double endLat, double endLon) {
//        final double startLatRadians = MathUtil.degrees2Radians() * (startLat);
//        final double startLonRadians = MathUtil.degrees2Radians() * (startLon);
//        final double endLatRadians = MathUtil.degrees2Radians() * (endLat);
//        final double endLonRadians = MathUtil.degrees2Radians() * (endLon);
//        double y = Math.sin(endLonRadians - startLonRadians) * Math.cos(endLatRadians);
//        double x = Math.cos(startLatRadians) * Math.sin(endLatRadians) -
//                Math.sin(startLatRadians) *
//                        Math.cos(endLatRadians) *
//                        Math.cos(endLonRadians - startLonRadians);
//        return MathUtil.wrap(MathUtil.radians2Degrees() * (Math.atan2(y, x)),
//                -180, 180);
//    }
//
//    /**
//     * 计算从指定方向的原点移动一段距离所产生的点(以从北开始顺时针的度数表示)。
//     * /// Returns the LatLng resulting from moving a distance from an origin
//     * /// in the specified heading (expressed in degrees clockwise from north).
//     * /// @param from     The LatLng from which to start.
//     * /// @param distance The distance to travel.
//     * /// @param heading  The heading in degrees clockwise from north.
//     */
//    public static LatLng computeOffset(LatLng from, double distance, double heading) {
//        distance /= getEarthRadius();
//        heading = MathUtil.toRadians(heading);
//        final double fromLat = MathUtil.toRadians(from.getLatitude());
//        final double fromLng = MathUtil.toRadians(from.getLongitude());
//        final double cosDistance = Math.cos(distance);
//        final double sinDistance = Math.sin(distance);
//        final double sinFromLat = Math.sin(fromLat);
//        final double cosFromLat = Math.cos(fromLat);
//        final double sinLat =
//                cosDistance * sinFromLat + sinDistance * cosFromLat * Math.cos(heading);
//        final double dLng = Math.atan2(sinDistance * cosFromLat * Math.sin(heading),
//                cosDistance - sinFromLat * sinLat);
//
//        return new LatLng(MathUtil.toDegrees(Math.asin(sinLat)),
//                MathUtil.toDegrees(fromLng + dLng), from.getType());
//    }
//
//    /**
//     * 计算原点的位置时，提供了一个点的目的地，行驶的米和原来的航向。
//     * 当提供拉丁目的地，行驶米和原始航向时，返回原点位置。标题以从北开始顺时针的度数表示。当没有解决方案时，此函数返回null可用。
//     * /// Returns the location of origin when provided with a LatLng destination,
//     * /// meters travelled and original heading. Headings are expressed in degrees
//     * /// clockwise from North. This function returns null when no solution is
//     * /// available.
//     * /// @param to       The destination LatLng.
//     * /// @param distance The distance travelled, in meters.
//     * /// @param heading  The heading in degrees clockwise from north.
//     */
//    public static LatLng computeOffsetOrigin(LatLng to, double distance, double heading) {
//        heading = MathUtil.toRadians(heading);
//        distance /= getEarthRadius();
//        // http://lists.maptools.org/pipermail/proj/2008-October/003939.html
//        final double n1 = Math.cos(distance);
//        final double n2 = Math.sin(distance) * Math.cos(heading);
//        final double n3 = Math.sin(distance) * Math.sin(heading);
//        final double n4 = Math.sin(MathUtil.toRadians(to.getLatitude()));
//        // There are two solutions for b. b = n2 * n4 +/- sqrt(), one solution
//        // results in the latitude outside the [-90, 90] range. We first try one
//        // solution and back off to the other if we are outside that range.
//        final double n12 = n1 * n1;
//        final double discriminant = n2 * n2 * n12 + n12 * n12 - n12 * n4 * n4;
//        if (discriminant < 0) {
//            // No real solution which would make sense in LatLng-space.
//            return null;
//        }
//        double b = n2 * n4 + Math.sqrt(discriminant);
//        b /= n1 * n1 + n2 * n2;
//        final double a = (n4 - n2 * b) / n1;
//        double fromLatRadians = Math.atan2(a, b);
//        if (fromLatRadians < -MathUtil.getPI() / 2 || fromLatRadians > MathUtil.getPI() / 2) {
//            b = n2 * n4 - Math.sqrt(discriminant);
//            b /= n1 * n1 + n2 * n2;
//            fromLatRadians = Math.atan2(a, b);
//        }
//        if (fromLatRadians < -MathUtil.getPI() / 2 || fromLatRadians > MathUtil.getPI() / 2) {
//            // No solution which would make sense in LatLng-space.
//            return null;
//        }
//        final double fromLngRadians = MathUtil.toRadians(to.getLongitude()) -
//                Math.atan2(n3, n1 * Math.cos(fromLatRadians) - n2 * Math.sin(fromLatRadians));
//        return new LatLng(MathUtil.toDegrees(fromLatRadians),
//                MathUtil.toDegrees(fromLngRadians), to.getType());
//    }
//
//    /**
//     * 计算原点和终点之间给定路程分数处的点。 位于出发地和目的地之间的给定路段的LatLng。
//     * /// Returns the LatLng which lies the given fraction of the way between the
//     * /// origin LatLng and the destination LatLng.
//     * /// @param from     The LatLng from which to start.
//     * /// @param to       The LatLng toward which to travel.
//     * /// @param fraction A fraction of the distance to travel.
//     * /// @return The interpolated LatLng.
//     */
//    public static LatLng interpolate(LatLng from, LatLng to, double fraction) {
//        // http://en.wikipedia.org/wiki/Slerp
//        final double fromLat = MathUtil.toRadians(from.getLatitude());
//        final double fromLng = MathUtil.toRadians(from.getLongitude());
//        final double toLat = MathUtil.toRadians(to.getLatitude());
//        final double toLng = MathUtil.toRadians(to.getLongitude());
//        final double cosFromLat = Math.cos(fromLat);
//        final double cosToLat = Math.cos(toLat);
//
//        // Computes Spherical interpolation coefficients.
//        final double angle = computeAngleBetween(from, to);
//        final double sinAngle = Math.sin(angle);
//        if (sinAngle < 1E-6) {
//            return new LatLng(from.getLatitude() + fraction * (to.getLatitude() - from.getLatitude()),
//                    from.getLongitude() + fraction * (to.getLongitude() - from.getLongitude()), from.getType());
//        }
//        final double a = Math.sin((1 - fraction) * angle) / sinAngle;
//        final double b = Math.sin(fraction * angle) / sinAngle;
//
//        // Converts from polar to vector and interpolate.
//        final double x = a * cosFromLat * Math.cos(fromLng) + b * cosToLat * Math.cos(toLng);
//        final double y = a * cosFromLat * Math.sin(fromLng) + b * cosToLat * Math.sin(toLng);
//        final double z = a * Math.sin(fromLat) + b * Math.sin(toLat);
//
//        // Converts interpolated vector back to polar.
//        final double lat = Math.atan2(z, Math.sqrt(x * x + y * y));
//        final double lng = Math.atan2(y, x);
//
//        return new LatLng(
//                MathUtil.toDegrees(lat), MathUtil.toDegrees(lng), from.getType());
//    }
//
//    /**
//     * 单位球上的距离;参数的单位是弧度。
//     * /// Returns distance on the unit sphere; the arguments are in radians.
//     */
//    public static double distanceRadians(double lat1, double lng1, double lat2, double lng2) {
//        return MathUtil.arcHav(MathUtil.havDistance(lat1, lat2, lng1 - lng2));
//    }
//
//    /**
//     * 两个拉丁文之间的夹角，以弧度为单位。这和单位球上的距离是一样的。
//     * /// Returns the angle between two LatLngs, in radians. This is the same as the
//     * /// distance on the unit sphere.
//     */
//    public static double computeAngleBetween(LatLng from, LatLng to) {
//        return distanceRadians(
//                MathUtil.toRadians(from.getLatitude()),
//                MathUtil.toRadians(from.getLongitude()),
//                MathUtil.toRadians(to.getLatitude()),
//                MathUtil.toRadians(to.getLongitude()));
//    }
//
//    /**
//     * 计算两点之间的距离，以米为单位。
//     * /// Returns the distance between two LatLngs, in meters.
//     */
//    public static double computeDistanceBetween(LatLng from, LatLng to) {
//        return computeAngleBetween(from, to) * getEarthRadius();
//    }
//
//    /**
//     * 计算给定路径在地球上的长度，以米为单位。
//     * /// Returns the length of the given path, in meters, on Earth.
//     */
//    public static double computeLength(List<LatLng> path) {
//        if (path == null || path.size() < 2) {
//            return 0;
//        }
//        final LatLng prev = path.get(0);
//        double prevLat = MathUtil.toRadians(prev.getLatitude());
//        double prevLng = MathUtil.toRadians(prev.getLongitude());
//        double length = 0;
//        for (LatLng point : path) {
//            final double lat = MathUtil.toRadians(point.getLatitude());
//            final double lng = MathUtil.toRadians(point.getLongitude());
//            length += distanceRadians(prevLat, prevLng, lat, lng);
//            prevLat = lat;
//            prevLng = lng;
//        }
//        return length * getEarthRadius();
//    }
//
//    /**
//     * 计算给定路径在地球上的长度，以米为单位。
//     * /// Returns the length of the given path, in meters, on Earth.
//     */
//    public static double computePointLength(List<Point> path) {
//        if (path == null || path.size() < 2) {
//            return 0;
//        }
//        final Point prev = path.get(0);
//        double prevLat = MathUtil.toRadians(prev.getY());
//        double prevLng = MathUtil.toRadians(prev.getX());
//        double length = 0;
//        for (Point point : path) {
//            final double lat = MathUtil.toRadians(point.getY());
//            final double lng = MathUtil.toRadians(point.getX());
//            length += distanceRadians(prevLat, prevLng, lat, lng);
//            prevLat = lat;
//            prevLng = lng;
//        }
//        return length * getEarthRadius();
//    }
//
//    /**
//     * 计算地球上一条闭合路径的面积。
//     * /// Returns the area of a closed path on Earth.
//     * /// @param path A closed path.
//     * /// @return The path's area in square meters.
//     */
//    public static double computeArea(List<LatLng> path) {
//        return Math.abs(computeSignedArea(path));
//    }
//
//    /**
//     * 计算地球上一条闭合路径的面积。
//     * /// Returns the area of a closed path on Earth.
//     * /// @param path A closed path.
//     * /// @return The path's area in square meters.
//     */
//    public static double computePointArea(List<Point> path) {
//        return Math.abs(computeSignedPointArea(path));
//    }
//
//    /**
//     * 地球上封闭路径上有标志的区域。该区域的标志可用于确定路径的方向。“内部”是指不包含南极的表面。
//     * /// Returns the signed area of a closed path on Earth. The sign of the area
//     * /// may be used to determine the orientation of the path.
//     * /// "inside" is the surface that does not contain the South Pole.
//     * /// @param path A closed path.
//     * /// @return The loop's area in square meters.
//     */
//    public static double computeSignedArea(List<LatLng> path) {
//        return _computeSignedArea(path, getEarthRadius());
//    }
//
//    /**
//     * 地球上封闭路径上有标志的区域。该区域的标志可用于确定路径的方向。“内部”是指不包含南极的表面。
//     * /// Returns the signed area of a closed path on Earth. The sign of the area
//     * /// may be used to determine the orientation of the path.
//     * /// "inside" is the surface that does not contain the South Pole.
//     * /// @param path A closed path.
//     * /// @return The loop's area in square meters.
//     */
//    public static double computeSignedPointArea(List<Point> path) {
//        return _computeSignedPointArea(path, getEarthRadius());
//    }
//
//    /**
//     * 给定半径的球面上封闭路径的带符号面积。计算面积的单位与半径的平方相同。
//     * /// Returns the signed area of a closed path on a sphere of given radius.
//     * /// The computed area uses the same units as the radius squared.
//     * /// Used by SphericalUtilTest.
//     *
//     * @return
//     */
//    private static double _computeSignedArea(List<LatLng> path, double radius) {
//        if (path == null || path.size() < 3) {
//            return 0;
//        }
//        final LatLng prev = path.get(path.size() - 1);
//        double prevTanLat = Math.tan((getPI() / 2 - prev.getLatitudeInRad()) / 2);
//        double prevLng = prev.getLongitudeInRad();
//        double total = 0;
//        // For each edge, accumulate the signed area of the triangle formed by the
//        // North Pole and that edge ("polar triangle").
//        for (LatLng point : path) {
//            final double lng = point.getLongitudeInRad();
//            final double lat = point.getLatitudeInRad();
//            final double tanLat = Math.tan((getPI() / 2 - lat) / 2);
//            total += _polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
//            prevTanLat = tanLat;
//            prevLng = lng;
//        }
//
//        return total * (radius * radius);
//    }
//
//    private static double _computeSignedPointArea(List<Point> path, double radius) {
//        if (path == null || path.size() < 3) {
//            return 0;
//        }
//        final Point prev = path.get(path.size() - 1);
//        double prevTanLat = Math.tan((getPI() / 2 - MathUtil.toRadians(prev.getY())) / 2);
//        double prevLng = MathUtil.toRadians(prev.getX());
//        double total = 0;
//        // For each edge, accumulate the signed area of the triangle formed by the
//        // North Pole and that edge ("polar triangle").
//        for (Point point : path) {
//            final double tanLat = Math.tan((getPI() / 2 - MathUtil.toRadians(point.getY())) / 2);
//            final double lng = MathUtil.toRadians(point.getX());
//            total += _polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
//            prevTanLat = tanLat;
//            prevLng = lng;
//        }
//
//        return total * (radius * radius);
//    }
//
//    /**
//     * 以北极为顶点的三角形的带符号面积。公式来源于“给定两条边和夹角的球面三角形的面积”，
//     * 参见Todhunter的“球面三角学”，71页，103节，第2点。
//     * /// Returns the signed area of a triangle which has North Pole as a vertex.
//     * /// Formula derived from "Area of a spherical triangle given two edges and
//     * /// the included angle" as per "Spherical Trigonometry" by Todhunter, page 71,
//     * /// section 103, point 2.
//     * /// See http://books.google.com/books?id=3uBHAAAAIAAJ&pg=PA71
//     * /// The arguments named "tan" are tan((pi/2 - latitude)/2).
//     */
//    private static double _polarTriangleArea(double tan1, double lng1, double tan2, double lng2) {
//        final double deltaLng = lng1 - lng2;
//        final double t = tan1 * tan2;
//        return 2 * Math.atan2(t * Math.sin(deltaLng), 1 + t * Math.cos(deltaLng));
//    }
//}
